Skip to main content

Production Run Convergence Plan

Last updated: 2026-03-23

This document details how to retire the send-to-partner design workflow and converge all partner production work into the Production Runs system. It covers the gap analysis, the specific changes needed, and the partner API/UI surface that needs to be unified.


Gap Analysis: What Send-to-Partner Has That Production Runs Doesn't

1. Long-running workflow tied to partner milestones

Send-to-Partner: Uses a single durable workflow (send-design-to-partner, store: true) with 6 async gate steps. Each partner action (start, finish, redo, refinish, inventory, complete) resumes the workflow via setStepSuccess. The workflow is the source of truth for progress — deterministic, ordered.

Production Runs: The dispatchProductionRunWorkflow only pauses for admin template selection (1-hour timeout). After tasks are created, the workflow completes immediately. Partner progress is tracked by the tasks.task.updated subscriber — eventually consistent, not workflow-driven.

Gap: Production Runs need a long-running workflow that spans the full partner execution lifecycle, not just dispatch.

2. Partner milestone endpoints

Send-to-Partner has dedicated partner endpoints:

EndpointAction
POST /partners/designs/:id/startPartner starts work, updates design status to In_Development
POST /partners/designs/:id/finishPartner marks work done, design → Technical_Review
POST /partners/designs/:id/redoPartner requests rework, creates redo child tasks
POST /partners/designs/:id/refinishPartner completes rework
POST /partners/designs/:id/completePartner submits inventory consumption, design → Approved

Production Runs only has:

EndpointAction
POST /partners/production-runs/:id/acceptPartner accepts run, status → in_progress

After acceptance, all interaction goes through generic task endpoints (/partners/assigned-tasks/). There is no production-run-scoped start, finish, redo, or complete endpoint.

Gap: Production Runs need run-scoped partner milestone endpoints that mirror the design flow.

3. Consumption logging

Send-to-Partner: Partners can log material consumption at any time during in_progress via POST /partners/designs/:id/consumption-logs. Logs track inventory_item_id, quantity, unit_of_measure, consumption_type (sample/production/wastage), and consumed_by.

Production Runs: No consumption logging endpoints exist on the partner side for production runs.

Gap: Add consumption log endpoints scoped to production runs.

4. Inventory adjustment on completion

Send-to-Partner: completePartnerDesignWorkflow adjusts inventory (negative quantities) and records consumption links on the design.

Production Runs: No inventory adjustment on run completion.

Gap: The production run completion flow needs to adjust inventory based on the run's snapshot and consumption logs.

5. Media upload by partner

Send-to-Partner: Partners can upload images and attach them to the design via /partners/designs/:id/media and /media/attach.

Production Runs: No media upload endpoint scoped to production runs.

Gap: Add media endpoints or reuse the existing design media flow (since production runs already have design_id).

6. Redo cycle

Send-to-Partner: Full redo loop — partner calls /redo, on-demand redo child tasks are created (partner-design-redo-log, redo-apply, redo-verify), partner works through them, then calls /refinish. Limited to one redo cycle per design. All-or-nothing — no per-piece granularity.

Production Runs: No explicit redo concept, but the task template system already supports it naturally.

Resolution (no gap): Redo in production runs = add more tasks from redo templates to the same run. The admin or partner creates new tasks from redo-specific templates (e.g., redo-inspect, redo-fix, redo-verify) and links them to the production run. The production-run-task-updated subscriber already checks all linked tasks before marking the run as complete — so new redo tasks automatically block completion until they're done.

This is better than send-to-partner's approach because:

  • Per-piece granularity: Each task can target a specific piece or step, not the whole design
  • Multiple redo cycles: No limit — add as many redo tasks as needed
  • Template-driven: Redo templates are configurable per category, not hardcoded to 3 fixed steps
  • No workflow gates needed: The existing subscriber-based completion check handles it

Proposed Convergence: Unified Partner API

New partner endpoints for Production Runs

Keep the existing task-based interaction (/partners/assigned-tasks/) as-is — it works well for granular task management. Add run-scoped endpoints for lifecycle operations:

EndpointActionWorkflow step signaled
POST /partners/production-runs/:id/acceptAlready exists. Accept run, status → in_progress
POST /partners/production-runs/:id/startPartner starts work on the runawait-run-start
POST /partners/production-runs/:id/finishPartner marks run work as doneawait-run-finish
POST /partners/production-runs/:id/completePartner submits final consumption, adjusts inventoryawait-run-complete
POST /partners/production-runs/:id/consumption-logsLog material usage during in_progress
GET /partners/production-runs/:id/consumption-logsRead consumption logs
POST /partners/production-runs/:id/mediaUpload media files
POST /partners/production-runs/:id/media/attachAttach uploaded URLs to the run's design

No /redo or /refinish endpoints needed — redo is handled by adding new tasks from redo templates to the run (see Redo via Task Templates below).

New long-running workflow: run-production-run-lifecycle

Replace the current "fire-and-forget" dispatch with a durable workflow that spans the partner execution lifecycle:

sendProductionRunToProductionWorkflow (existing, creates tasks)


runProductionRunLifecycleWorkflow (NEW, store: true)

├─ await-run-start (async, 23-day timeout)
│ Partner calls POST /start
│ → run status: "in_progress"
│ → design status: "In_Development" (if sampling)

├─ await-run-finish (async, 23-day timeout)
│ Partner calls POST /finish
│ → run metadata: finished_at
│ → design status: "Technical_Review" (if sampling)

├─ await-run-complete (async, 23-day timeout)
│ Partner calls POST /complete
│ → adjusts inventory
│ → records consumption
│ → run status: "completed"
│ → design status: "Approved" (if sampling)

└─ cascadeCompletionStep
→ check parent run
→ auto-dispatch dependent siblings
→ mark parent complete if all children done

This workflow is started by sendProductionRunToProductionWorkflow after task creation. It has no redo gates — redo is handled at the task level, outside the workflow.

Redo via Task Templates

Redo doesn't need workflow gates or dedicated endpoints. It's just more tasks from redo templates added to the same run.

Run dispatched with templates: [cutting, stitching, finishing]

├─ Partner completes cutting ✓
├─ Partner completes stitching ✓
├─ Finishing has issues

│ Admin adds redo tasks from templates: [redo-inspect, redo-fix, redo-verify]
│ (linked to the same production run)

├─ Partner completes redo-inspect ✓
├─ Partner completes redo-fix ✓
├─ Partner completes redo-verify ✓
├─ Partner completes finishing ✓

└─ All tasks done → subscriber marks run as completed

This works because:

  1. The subscriber checks all linked tasks. production-run-task-updated fetches every task linked to the run (excluding the container task) and only marks the run as completed when all are done. New redo tasks automatically become blockers.

  2. No model changes needed. The Task model, the link table, and the subscriber all support arbitrary numbers of tasks per run already.

  3. Admin endpoint for adding redo tasks. Use the existing POST /admin/production-runs/:id/send-to-production-style flow or add a simpler POST /admin/production-runs/:id/tasks that creates tasks from template names and links them to the run + partner + design.

  4. Better than send-to-partner's redo because:

    • Per-piece or per-step granularity (not all-or-nothing)
    • Multiple redo rounds (not limited to one cycle)
    • Template-driven (configurable per category, not hardcoded to 3 steps)
    • No workflow gate overhead

How this interacts with existing task flow

The two systems coexist:

  • Run-level milestones (start/finish/complete) are signaled via the lifecycle workflow and update the run's status and metadata
  • Task-level granularity (accept/finish individual tasks, complete subtasks, redo tasks) continues via /partners/assigned-tasks/ and the tasks.task.updated subscriber
  • The lifecycle workflow's await-run-complete step is the authoritative completion gate — the subscriber is the secondary check that handles the task→run completion cascade

This means a partner can either:

  1. Work through individual subtasks → auto-complete parent tasks → then call /complete on the run, OR
  2. Call /finish on the run directly (which bulk-completes remaining tasks)

Both paths lead to the same outcome.

Partner UI changes

The partner-ui currently has two separate sections:

  • Designs (/designs/*) — uses design milestone endpoints
  • Production Runs (/production-runs/*) — uses accept + task endpoints

After convergence, the Designs section is retired from the partner portal. Everything lives under Production Runs:

/production-runs                    → List all assigned runs (sampling + production)
/production-runs/:id → Run detail with:
- Run info (status, quantity, role, snapshot)
- Action buttons (Start, Finish, Redo, Complete)
- Task list with accept/finish per task
- Subtask checkboxes
- Consumption logs section
- Media upload section
/production-runs/:id/complete → Completion modal (inventory consumption form)
/production-runs/:id/media → Media upload drawer

The action buttons follow the same status-gating logic as the current design actions:

Run statusActions available
sent_to_partnerAccept
in_progress (not started)Start, Log Consumption, Upload Media
in_progress (started)Finish, Log Consumption, Upload Media
finishedComplete
completedView only

Redo tasks appear in the task list like any other task — no separate phase or action button needed.

Sampling vs Production distinction

Add a run_type field to the ProductionRun model:

run_type: model.enum(["production", "sample"]).default("production")

Behavioral differences:

Sample runProduction run
Created byAdmin manually from design pageAuto from order.placed or admin manually
Order linkedNoYes
Design status updatesYes (In_Development → Technical_Review → Approved)No (design already Commerce_Ready)
QuantityTypically 1From order line item
Listed in partner UIUnder "Sampling" tab/filterUnder "Production" tab/filter

The lifecycle workflow checks run_type to decide whether to update design status at each milestone.


Implementation Order

Step 1 — Add run_type to ProductionRun model

  • Add run_type enum field with default "production"
  • Migration
  • Update admin create endpoint to accept run_type
  • Update partner list endpoint to support run_type filter

Step 2 — Create runProductionRunLifecycleWorkflow

  • New workflow at src/workflows/production-runs/run-production-run-lifecycle.ts
  • Async gate steps: await-run-start, await-run-finish, await-run-complete
  • 23-day timeout per step (matching current design workflow)
  • Triggered by sendProductionRunToProductionWorkflow after task creation
  • Uses store: true for durability
  • No redo gates — redo is handled via task templates at the task level

Step 3 — Add partner milestone endpoints

  • POST /partners/production-runs/:id/start
  • POST /partners/production-runs/:id/finish
  • POST /partners/production-runs/:id/complete
  • Each endpoint: validates partner ownership, updates run metadata, signals workflow step
  • /complete runs inventory adjustment + consumption recording (port from completePartnerDesignWorkflow)

Step 4 — Add admin endpoint for redo tasks

  • POST /admin/production-runs/:id/tasks — creates tasks from template names, links to run + partner + design
  • Reuses createTaskWithTemplates from TaskService
  • New tasks automatically block run completion via the existing subscriber

Step 5 — Add consumption log endpoints on production runs

  • POST /partners/production-runs/:id/consumption-logs
  • GET /partners/production-runs/:id/consumption-logs
  • Reuse logConsumptionWorkflow and listConsumptionLogsWorkflow with production_run_id context

Step 6 — Add media endpoints on production runs

  • POST /partners/production-runs/:id/media
  • POST /partners/production-runs/:id/media/attach
  • Route media to the run's linked design (since designs own media)

Step 7 — Update partner-ui

  • Add milestone action buttons to production run detail page
  • Add consumption logs section
  • Add media upload section
  • Add run_type filter (Sampling / Production tabs)
  • Retire /designs/* routes from partner-ui

Step 8 — Admin UI updates

  • Add "Create Sample Run" action on design detail page
  • Update batch send drawer to create production runs instead of calling send-to-partner
  • Add run_type column to production runs list

Step 9 — Deprecate send-to-partner

  • Mark POST /admin/designs/:id/send-to-partner as deprecated (log warning)
  • Remove admin UI entry points
  • Keep the endpoint functional for in-flight workflows

Step 10 — Remove send-to-partner

  • Remove the workflow, route, validators, and admin UI components
  • Remove /partners/designs/:id/start|finish|redo|refinish|complete endpoints
  • Remove partner-ui design routes
  • Clean up old task templates if no longer referenced

Files to Create

FilePurpose
src/workflows/production-runs/run-production-run-lifecycle.tsLong-running lifecycle workflow
src/api/partners/production-runs/[id]/start/route.tsPartner start endpoint
src/api/partners/production-runs/[id]/finish/route.tsPartner finish endpoint
src/api/partners/production-runs/[id]/complete/route.tsPartner complete endpoint
src/api/admin/production-runs/[id]/tasks/route.tsAdmin endpoint to add redo tasks
src/api/partners/production-runs/[id]/consumption-logs/route.tsConsumption log CRUD
src/api/partners/production-runs/[id]/media/route.tsMedia upload
src/api/partners/production-runs/[id]/media/attach/route.tsMedia attach

Files to Modify

FileChange
src/modules/production_runs/models/production-run.tsAdd run_type field
src/workflows/production-runs/send-production-run-to-production.tsTrigger lifecycle workflow after task creation
src/api/partners/production-runs/route.tsAdd run_type filter
src/api/partners/production-runs/[id]/route.tsReturn consumption logs + media in detail
src/api/middlewares.tsRegister new partner routes
apps/partner-ui/src/routes/production-runs/Add milestone buttons, consumption, media

Files to Remove (Step 9)

FileReason
src/workflows/designs/send-to-partner.tsReplaced by production run lifecycle
src/api/admin/designs/[id]/send-to-partner/Deprecated endpoint
src/admin/components/designs/batch-send-to-partner-drawer.tsxUI for deprecated flow
src/api/partners/designs/[designId]/start/Replaced by run milestone
src/api/partners/designs/[designId]/finish/Replaced by run milestone
src/api/partners/designs/[designId]/redo/Replaced by run milestone
src/api/partners/designs/[designId]/refinish/Replaced by run milestone
src/api/partners/designs/[designId]/complete/Replaced by run milestone
apps/partner-ui/src/routes/designs/Replaced by production runs UI